//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "Policies.h"
#include <stdio.h>
#include <stdlib.h>

Policy::Policy()
{
    CurrentPolicy=0;
    pusage_records=new FixedList(LENGTH);
    pmode_records=new FixedList(LENGTH);
}

Policy::Policy(PolicyType type)
{
    if(type < AVG) type = AVG;
    if(type > PAST) type = PAST;
    CurrentPolicy=type;
}

PolicyType Policy::GetPolicy()
{
    return (PolicyType)CurrentPolicy;
}

void Policy::SetPolicy(PolicyType type)
{
    CurrentPolicy=type;
}

Policy::~Policy()
{
    delete pusage_records;
    delete pmode_records;
}

Mode Policy::GetCpuMode(UInt32 usage)
{
    UInt32 mode=0;
    switch (CurrentPolicy) {
        case AVG :
            mode=DoAVG(usage);
            pmode_records->AddItem(mode);
            break;

        case LONGRUN :
            pusage_records->AddItem(usage);
            mode=GetMode(usage);
            pmode_records->AddItem(mode);
            break;

        case PAST :
            pusage_records->AddItem(usage);
            mode=GetMode(usage);
            pmode_records->AddItem(mode);
            break;

        case PEAK :
            mode=DoPeak(usage);
            pmode_records->AddItem(mode);
            break;

        case STEPPED :
            mode=DoStepped(usage);
            pmode_records->AddItem(mode);
            break;

        default:
            mode=Heightest;//is it a little strange?
            pmode_records->AddItem(mode);
            break;
    }
    return (Mode)mode;
}
UInt32 Policy::DoAVG(UInt32 usage)
{
    pusage_records->AddItem(usage);
    UInt32 sum=0;
    UInt32 cur_len=pusage_records->GetCurrentLength();
    UInt32 * usages=new UInt32[cur_len];
    pusage_records->GetArray(usages,cur_len);
    for (UInt32 i = 0;i < cur_len;i++) {
        sum+=usages[i];
    }
    delete usages;
    return GetMode(sum / cur_len);
}
UInt32 Policy::DoPeak(UInt32 usage)
{/*based on the following three rules:
    *1,if 4 previous mode is  fallling,then lower it still until to lowest.
    *2,if 4 previous mode is higher than medium,then lower it coz we think it will pass it's peak.
    *3,if 4 previous mode is lower than medium and keep flat,then nothing.
    */
    pusage_records->AddItem(usage);
    int cur_len=pmode_records->GetCurrentLength();
    if(cur_len < PEAKLENGTH)return GetMode(usage);//not enough to get a peak mode.

    UInt32 * modes=new UInt32[cur_len];
    pmode_records->GetArray(modes,cur_len);
    bool fit_rule1=true,fit_rule2=true,fit_rule3=true;

    //check for rule1
    UInt32 i=0;
    for (;i < PEAKLENGTH-1;i++) {
        if ((modes[i] > modes[i + 1]) && fit_rule1) {
            continue;
        }
        else {
            fit_rule1=false;
            break;
        }
    }

    //check for rule2
    for (i = 0;i < PEAKLENGTH;i++) {
        if ((modes[i] > Medium) && fit_rule2) {
            continue;
        }
        else {
            fit_rule2=false;
            break;
        }
    }

    //check for rule3
    for (i = 0;i < PEAKLENGTH;i++) {
        if ((modes[i] < Medium) && fit_rule3) {
            continue;
        }
        else {
            fit_rule3=false;
            break;
        }
    }

    //decide the mode base on the three rules.
    UInt32 tempMode1=0,tempMode2=0,tempMode3=0;
    if(fit_rule1)tempMode1 = (modes[PEAKLENGTH-1] > 1 ? modes[PEAKLENGTH-1] - 1 : 0);// lower it.
    if(fit_rule2)tempMode2 = Medium;//lower to medium
    if(fit_rule3)tempMode3 = modes[PEAKLENGTH-1];//no change.

    //choose the lowest.
    UInt32 tempMode=tempMode1 < tempMode2 ? tempMode1 : tempMode2;
    return tempMode < tempMode3 ? tempMode: tempMode3;
}

UInt32 Policy::DoStepped(UInt32 usage)
{//everytime just increase(decrease) one step.
    if(pmode_records->GetCurrentLength() < 1)return Lowest;

    UInt32 lastUsage=pusage_records->GetLastItem();
    UInt32 mode=0;

    if (usage >= lastUsage) {
        if (mode + 1 <= Heightest)
            mode+=1;
        else
            mode=Heightest;
    }
    else {
        if (mode > 1)
            mode-=1;
        else
            mode=1;
    }
    pusage_records->AddItem(usage);
    return mode;
}

UInt32 Policy::GetMode(UInt32 usage)
{
    if(usage < 10)return Lowest;
    if(usage < 25)return VeryLow;
    if(usage < 40)return Low;
    if(usage < 60)return Medium;
    if(usage < 75)return High;
    if(usage < 90)return VeryHigh;
    return Heightest;
}
